package com.pshcp.patient.api;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.external.common.WebServiceException;
import com.external.pay.domain.IcbcNotifyAO;
import com.external.pay.domain.IcbcNotifyBizContentDTO;
import com.external.pay.wechat.WechatPayNotifyService;
import com.framework.base.ResultBuilder;
import com.framework.logaop.Log;
import com.framework.logaop.LogSourceEnum;
import com.framework.logaop.LogTypeEnum;
import com.icbc.api.IcbcApiException;
import com.icbc.api.utils.IcbcSignature;
import com.icbc.api.utils.WebUtils;
import com.pshcp.common.enums.PayBusinessEnum;
import com.pshcp.common.enums.RegPayStatusEnum;
import com.pshcp.domain.model.OrderReg;
import com.pshcp.domain.model.PatientMedicalRecordsPrintApply;
import com.pshcp.domain.model.PatientOnlineOrder;
import com.pshcp.domain.model.PatientUnpayFeeRecord;
import com.pshcp.patient.business.OnlineOrderBusiness;
import com.pshcp.patient.business.OrderRegBusiness;
import com.pshcp.patient.business.PatientMedicalPrintBusiness;
import com.pshcp.patient.business.PatientUnpaidFeeBusiness;
import com.pshcp.patient.domain.paynotify.PayNotifyDTO;
import com.pshcp.patient.tool.PayNotifyTools;
import com.pshcp.service.OrderRegService;
import com.pshcp.service.PatientMedicalRecordsPrintApplyService;
import com.pshcp.service.PatientOnlineOrderService;
import com.pshcp.service.PatientUnpayFeeRecordService;
import com.pshcp.service.PayInfoService;
import com.wechat.pay.java.service.payments.model.Transaction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;

@ApiIgnore
@Slf4j
@RestController
@RequestMapping("/api/payNotify")
public class PayNotifyAPI {

    @Value("${custom.pay.icbc.gwPubKey}")
    private String gwPubKey;

    @Value("${custom.pay.icbc.priKey}")
    private String priKey;

    private static String failResp = "{\"return_code\":-1,\"return_msg\":\"icbc sign not pass.\"}";

//    /**
//     * 商户API V3 key
//     */
//    @Value("${custom.pay.wechat.apiV3key}")
//    private String apiV3key;
//


    @Autowired
    private WechatPayNotifyService wechatPayNotifyService;

    @Autowired
    private OrderRegService orderRegService;

    @Autowired
    private PatientOnlineOrderService onlineOrderService;

    @Autowired
    private OrderRegBusiness orderRegBusiness;

    @Autowired
    private OnlineOrderBusiness onlineOrderBusiness;

    @Autowired
    private PatientUnpayFeeRecordService unpayFeeRecordService;

    @Autowired
    private PatientUnpaidFeeBusiness unpaidFeeBusiness;

    @Autowired
    private PatientMedicalRecordsPrintApplyService printApplyService;

    @Autowired
    private PatientMedicalPrintBusiness medicalPrintBusiness;


    @Autowired
    private PayInfoService payInfoService;

    @Log(source = LogSourceEnum.APP, type = LogTypeEnum.SELECT, desc = "ICBC回调")
    @PostMapping("icbc/{payType}")
    public String icbc(@PathVariable Integer payType, @RequestBody IcbcNotifyAO icbcNotifyAO) throws IcbcApiException {
        try {
            String req = JSON.toJSONString(icbcNotifyAO);
            HashMap params = JSON.parseObject(req, HashMap.class);
            String signStr = WebUtils.buildOrderedSignStr("/api/payNotify/icbc/"+payType, params);

            boolean flag = IcbcSignature.verify(signStr, icbcNotifyAO.getSign_type(), gwPubKey, icbcNotifyAO.getCharset(), icbcNotifyAO.getSign());
            if (!flag) {
                return buildIcbcResp(failResp, icbcNotifyAO.getCharset());
            }
            //business handler
            IcbcNotifyBizContentDTO bizContentDTO = JSON.parseObject(icbcNotifyAO.getBiz_content(), IcbcNotifyBizContentDTO.class);
            if(!"0".equals(bizContentDTO.getReturn_code())){
                return buildIcbcResp(failResp, icbcNotifyAO.getCharset());
            }
            PayNotifyDTO payNotifyDTO = PayNotifyTools.buildIcbcPayNotify(payType, bizContentDTO);
            handlerPayNotify(payNotifyDTO);
            //resp
            String responseBizContent="{\"return_code\":0,\"return_msg\":\"success\",\"msg_id\":\""+bizContentDTO.getMsg_id()+"\","
                    +"\"busi_param_rp\":\"thisisresponseparameter\"}";
            return buildIcbcResp(responseBizContent, icbcNotifyAO.getCharset());
        }catch (IcbcApiException | WebServiceException e){
            log.error("icbc notify handler fail", e);
        }
        return buildIcbcResp(failResp, icbcNotifyAO.getCharset());
    }

    /**
     * icbc resp
     * @param responseBizContent
     * @param charset
     * @return
     * @throws IcbcApiException
     */
    private String buildIcbcResp(String responseBizContent, String charset) throws IcbcApiException {
        String signStr="\"response_biz_content\":"+responseBizContent+","+"\"sign_type\":"+"\"RSA\"";
        String sign=IcbcSignature.sign(signStr, "RSA", priKey, charset,"");
        return "{"+signStr+",\"sign\":\""+sign+"\"}";
    }

    @Log(source = LogSourceEnum.APP, type = LogTypeEnum.SELECT, desc = "wechat回调")
    @PostMapping("wechat/{payType}")
    public String wechat(@PathVariable Integer payType, HttpServletRequest request) throws WebServiceException, IOException {
        // 验签并解密报文
        Transaction transaction = wechatPayNotifyService.validSign(request);
        if(!Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())){
            log.error("支付回调通知支付状态未成功，业务类型：{},业务单号：{}", payType, transaction.getOutTradeNo());
            return null;
        }
        // handler notify
        PayNotifyDTO payNotifyDTO = PayNotifyTools.buildWechatPayNotify(payType, transaction);
        handlerPayNotify(payNotifyDTO);
        JSONObject result = new JSONObject();
        result.put("code", "SUCCESS");
        result.put("message","");
        return result.toJSONString();
    }

    /**
     * 业务逻辑处理
     * @param payNotifyDTO
     */
    private void handlerPayNotify(PayNotifyDTO payNotifyDTO) throws WebServiceException {
        log.info("notify handler start, payType:{}", payNotifyDTO.getPayBusiness());
        Long orderId = payNotifyDTO.getBizId();
        if(PayBusinessEnum.REG_PAY.getCode().equals(payNotifyDTO.getPayBusiness())){
            OrderReg orderReg = orderRegService.findById(orderId);
            if(RegPayStatusEnum.WAIT_PAY.getCode().equals(orderReg.getPayStatus())){
                orderRegBusiness.enterRegOrder(orderReg);
                log.info("order reg enter complete");
            }else {
                log.error("order reg status not equals wait pay, ignore update order status!!!, orderId:{}", orderId);
            }
        }
        if(PayBusinessEnum.ONLINE_PAY.getCode().equals(payNotifyDTO.getPayBusiness())){
            PatientOnlineOrder onlineOrder = onlineOrderService.findById(orderId);
            onlineOrderBusiness.enterOnlineOrder(onlineOrder);
            log.info("online order enter complete");
        }
        if(PayBusinessEnum.UNPAID_FEE_PAY.getCode().equals(payNotifyDTO.getPayBusiness())){
            PatientUnpayFeeRecord feeRecord = unpayFeeRecordService.findById(orderId);
            unpaidFeeBusiness.enterOrder(feeRecord, payNotifyDTO.getPayMode(), payNotifyDTO.getThirdNo());
            log.info("unpaid fee order enter complete");
        }
        if(PayBusinessEnum.MEDICAL_PRINT_FEE_PAY.getCode().equals(payNotifyDTO.getPayBusiness())){
            PatientMedicalRecordsPrintApply printApply = printApplyService.findById(orderId);
            medicalPrintBusiness.enterOrder(printApply);
            log.info("medical print fee order enter complete");
        }
        //insert pay info
        payInfoService.insert(PayNotifyTools.buildPayInfo(payNotifyDTO));
    }

}
